package net.linku.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.Cached;
import feign.FeignException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.linku.constant.Constants;
import net.linku.converter.AccountMapperConverter;
import net.linku.converter.MenuMapperConverter;
import net.linku.converter.PrivilegeMapperConverter;
import net.linku.converter.RoleMapperConverter;
import net.linku.domain.LAAccount;
import net.linku.domain.LAMenu;
import net.linku.domain.LAPrivilege;
import net.linku.domain.LARole;
import net.linku.feign.Oauth2FeignClient;
import net.linku.feign.response.JwtToken;
import net.linku.model.req.login.UserRequestParams;
import net.linku.model.resp.login.*;
import net.linku.service.*;
import net.linku.utils.TenantContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 管理员账户服务实现类
 * <p>
 * 实现了 IAdminAccountService 接口，提供管理员账户的具体操作实现，例如登录。
 * </p>
 *
 * @Author SilenceTian
 * @Date 2024/7/12
 * @Version 1.0
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class AdminAccountServiceImpl implements IAdminAccountService {

    @Value("${basic.token:Basic bGlua3UtYXBpOmxpbmt1LXNlY3JldA==}")
    private String basicToken;

    private final Oauth2FeignClient oauth2FeignClient;
    private final LAMenuService laMenuService;
    private final LAPrivilegeService laPrivilegeService;
    private final LARoleService laRoleService;
    private final LAAccountInfoService laAccountInfoService;
    private final RedisTemplate<String, Object> redisTemplate;

    /**
     * 管理员账户登录
     *
     * @param accountCode 管理员账户的名称
     * @param password    管理员账户的密码
     * @return 返回登录结果，包括 token 和用户信息
     */
    @Override
    @Cached(name = "loginCache", key = "#accountCode", expire = 3600, cacheType = CacheType.BOTH)
    public LoginResult accountLogin(String accountCode, String password) {
        Long tenantId = Long.valueOf(TenantContext.getTenantId()); // 从上下文获取租户ID
        log.info("{} 租户下的用户 {} 开始登录", tenantId, accountCode);
        JwtToken jwtToken = fetchJwtToken(accountCode, password, basicToken, tenantId);
        log.info("远程调用授权服务器成功，获取的 token 为 {}", JSON.toJSONString(jwtToken, true));

        String token = jwtToken.getAccessToken();
        UserInfo userInfo = parseJwtToken(tenantId, token, jwtToken.getExpiresIn());

        return new LoginResult(jwtToken.getTokenType() + " " + token, userInfo);
    }

    /**
     * 调用 OAuth2 授权服务器获取 JWT Token
     *
     * @param accountCode 账户名
     * @param password    密码
     * @param basicToken  基础 token
     * @return JWT Token 响应对象
     */
    private JwtToken fetchJwtToken(String accountCode, String password, String basicToken, Long tenantId) {
        try {
            ResponseEntity<JwtToken> response = oauth2FeignClient.getToken(accountCode, password, Constants.GRANT_TYPE_PASSWORD, Constants.ADMIN_TYPE, basicToken, tenantId);
            return response.getBody();
        } catch (FeignException e) {
            log.error("获取 token 失败: {}", e.getMessage());
            throw e;
        }
    }

    /**
     * 解析 JWT Token 并组装用户信息
     *
     * @param tenantId  租户信息
     * @param token     JWT Token
     * @param expiresIn 过期时间
     * @return 用户信息对象
     */
    private UserInfo parseJwtToken(Long tenantId, String token, Long expiresIn) {
        Jwt jwt = JwtHelper.decode(token);
        String jwtJson = jwt.getClaims();
        JSONObject json = JSON.parseObject(jwtJson);
        Long userId = Long.valueOf(json.getString("user_name"));
        UserRequestParams userRequestParams = UserRequestParams.builder().userId(userId).tenantId(tenantId).build();
        // 获取菜单数据
        List<LAMenuVO> menus = getMenusByAccountId(userRequestParams);
        // 获取用户的账户信息列表
        List<LAAccountVO> accounts = getAccountInfoByAccountId(userRequestParams);
        // 获取权限数据
        List<LAPrivilegeVO> privileges = getPrivilegesByAccountId(userRequestParams);
        // 获取角色数据
        List<LARoleVO> roles = getRolesByAccountId(userRequestParams);
        JSONArray authorities = json.getJSONArray("authorities");
        List<SimpleGrantedAuthority> permissions = authorities.stream()
                .map(authorityJson -> new SimpleGrantedAuthority(authorityJson.toString()))
                .collect(Collectors.toList());

        // 将 token 存入 Redis
        redisTemplate.opsForValue().set(token, "", expiresIn, TimeUnit.SECONDS);

        // 组装用户信息
        return UserInfo.builder()
                .userId(userId)
                .roles(roles)
                .privileges(privileges)
                .permissions(permissions)
                .accounts(accounts)
                .menus(menus)
                .build();
    }

    /**
     * 根据用户名称获取角色数据
     *
     * @param userRequestParams 用户请求参数，包含租户ID和用户ID。
     * @return 符合条件的 LARoleVO 列表。
     */
    private List<LARoleVO> getRolesByAccountId(UserRequestParams userRequestParams) {
        List<LARole> roles = laRoleService.getRolesByAccountId(userRequestParams);
        return RoleMapperConverter.INSTANCE.covertToRoleVO(roles);
    }

    /**
     * 根据用户名称获取权限数据
     *
     * @param userRequestParams 用户请求参数，包含租户ID和用户ID。
     * @return 符合条件的 LAPrivilegeVO 列表。
     */
    private List<LAPrivilegeVO> getPrivilegesByAccountId(UserRequestParams userRequestParams) {
        List<LAPrivilege> privileges = laPrivilegeService.getPrivilegesByAccountId(userRequestParams);
        return PrivilegeMapperConverter.INSTANCE.covertToPrivilegeVO(privileges);
    }

    /**
     * 根据用户名称获取菜单
     *
     * @param userRequestParams 用户请求参数，包含租户ID和用户ID。
     * @return 符合条件的 LAMenuVO 列表。
     */
    private List<LAMenuVO> getMenusByAccountId(UserRequestParams userRequestParams) {
        List<LAMenu> menus = laMenuService.getMenusByAccountId(userRequestParams);
        return MenuMapperConverter.INSTANCE.covertToMenusVO(menus);
    }

    /**
     * 根据用户名称获取账户信息
     *
     * @param userRequestParams 用户请求参数，包含租户ID和用户ID。
     * @return 符合条件的 LAAccountVO 列表。
     */
    private List<LAAccountVO> getAccountInfoByAccountId(UserRequestParams userRequestParams) {
        List<LAAccount> accounts = laAccountInfoService.getAccountInfoByAccountId(userRequestParams);
        return AccountMapperConverter.INSTANCE.covertToAccountsVO(accounts);
    }
}
